﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;
using System.Threading;
using System.Xml.XPath;

namespace Configuration
{
    public static class Complementary
    {
        public static Semaphore semConfig = new Semaphore(1, 1);
        public static string configFile = "config\\configuration.xml";
        public static string path = string.Concat(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "\\");
        public static string pluginsPath = ".\\plugins\\";
    }

    public static class Configuration
    {
        public static int maxThreads = 0;
        public static int maxConnectionTime = 0;
        public static int maxIddleReceiving = 0;
        public static int maxIddleSending = 0;

        // If true, you can connect to any remote host using this proxy.
        public static int port = 8080;

        public static bool openProxy = false;
        public static List<RelationDomainIp> lstRelationDomainIp = new List<RelationDomainIp>();

        // shows debug messages in error responses (ex: 500)
        public static bool debug = true;

        public static string reverseProxyCertFile = string.Empty;
        public static string reverseProxyCertPassword = string.Empty;

        public static string logsPath = string.Empty;

        // Cookie name for sessions
        public static string cookieSessionName = string.Empty;

        static public bool LoadConfig()
        {
            try
            {
                maxThreads = int.Parse(GetValue("/configuration/maxThreads"));
                maxConnectionTime = int.Parse(GetValue("/configuration/maxConnectionTime"));
                maxIddleReceiving = int.Parse(GetValue("/configuration/maxIddleReceiving"));
                maxIddleSending = int.Parse(GetValue("/configuration/maxIddleSending"));

                debug = bool.Parse(GetValue("/configuration/debug"));
                openProxy = bool.Parse(GetValue("/configuration/openproxy"));
                port = int.Parse((GetValue("/configuration/port")));
                reverseProxyCertFile = GetValue("/configuration/reverseProxyCertificate");
                if (!System.IO.Path.IsPathRooted(reverseProxyCertFile))
                    reverseProxyCertFile = string.Concat(Complementary.path, reverseProxyCertFile);
                    
                reverseProxyCertPassword = GetValue("/configuration/reverseProxyCertificatePassword");
                logsPath = GetValue("/configuration/logsPath");
                cookieSessionName = GetValue("/configuration/cookieName");
                lstRelationDomainIp = GetForwardRelations();
            }
            catch
            {
                return false;
            }
            return true;
        }

        public static string GetValue(string xpath)
        {
            Complementary.semConfig.WaitOne();

            try
            {
                if (!File.Exists(string.Concat(Complementary.path, Complementary.configFile)))
                    return null;

                string xml = File.ReadAllText(string.Concat(Complementary.path, Complementary.configFile));

                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Fragment;

                XPathDocument doc = new XPathDocument(XmlReader.Create(new StringReader(xml), settings));

                XPathNavigator nav = doc.CreateNavigator();
                return nav.SelectSingleNode(xpath).Value;

            }
            catch
            {

            }
            finally
            {
                Complementary.semConfig.Release();
            }

            return null;
        }

        public static List<string> GetEnabledPlugins()
        {
            List<string> lstPlugins = new List<string>();

            Complementary.semConfig.WaitOne();

            try
            {
                if (!File.Exists(string.Concat(Complementary.path, Complementary.configFile)))
                    return lstPlugins;

                string xml = File.ReadAllText(string.Concat(Complementary.path, Complementary.configFile));

                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Fragment;

                XPathDocument doc = new XPathDocument(XmlReader.Create(new StringReader(xml), settings));

                XPathNavigator nav = doc.CreateNavigator();
                XPathNodeIterator pluginNode = nav.Select("//configuration/plugin");

                while (pluginNode.MoveNext())
                {
                    string file = pluginNode.Current.GetAttribute("file", "").ToLower();
                    bool enabled = bool.Parse(pluginNode.Current.GetAttribute("enabled", ""));
                    
                    if (enabled)
                        lstPlugins.Add(file);
                }
            }
            catch
            {

            }
            finally
            {
                Complementary.semConfig.Release();
            }

            return lstPlugins;
        }

        public static bool IsPluginEnabled(string filename)
        {

            Complementary.semConfig.WaitOne();

            try
            {
                if (!File.Exists(string.Concat(Complementary.path, Complementary.configFile)))
                    return false;

                string xml = File.ReadAllText(string.Concat(Complementary.path, Complementary.configFile));

                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Fragment;

                XPathDocument doc = new XPathDocument(XmlReader.Create(new StringReader(xml), settings));

                XPathNavigator nav = doc.CreateNavigator();
                XPathNodeIterator plugins = nav.Select("//configuration/plugin");

                while (plugins.MoveNext())
                {
                    if (plugins.Current.GetAttribute("file", "").ToLower() == filename.ToLower())
                    {
                        if (plugins.Current.GetAttribute("enabled", "").ToLower() == "true")
                            return true;
                    }
                }
            }
            catch
            {

            }
            finally
            {
                Complementary.semConfig.Release();
            }

            return false;
        }

        public static List<RelationDomainIp> GetForwardRelations()
        {
            List<RelationDomainIp> lstRelations = new List<RelationDomainIp>();

            Complementary.semConfig.WaitOne();

            try
            {
                if (!File.Exists(string.Concat(Complementary.path, Complementary.configFile)))
                    return lstRelations;

                string xml = File.ReadAllText(string.Concat(Complementary.path, Complementary.configFile));

                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Fragment;

                XPathDocument doc = new XPathDocument(XmlReader.Create(new StringReader(xml), settings));

                XPathNavigator nav = doc.CreateNavigator();
                XPathNodeIterator relationNode = nav.Select("//configuration/forwards/forward");

                while (relationNode.MoveNext())
                {
                    string vhost = relationNode.Current.GetAttribute("vHost", "").ToLower();
                    string remVHost = relationNode.Current.GetAttribute("remoteHost", "").ToLower();
                    string ip = relationNode.Current.GetAttribute("remoteIp", "").ToLower();
                    int port = int.Parse(relationNode.Current.GetAttribute("remotePort", ""));

                    RelationDomainIp relation = new RelationDomainIp(vhost, remVHost, ip, port);
                    lstRelations.Add(relation);
                }
            }
            catch
            {

            }
            finally
            {
                Complementary.semConfig.Release();
            }

            return lstRelations;
        }
    }


    public class RelationDomainIp
    {
        public string domain = string.Empty;
        public string remoteIp = string.Empty;
        public string remoteHostName = string.Empty;
        public int remotePort = 0;

        public RelationDomainIp(string domain, string remoteHostName, string remoteIp, int remotePort)
        {
            this.domain = domain;
            this.remoteIp = remoteIp;
            this.remotePort = remotePort;
            this.remoteHostName = remoteHostName;
        }
    }


}
